home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1998 March
/
Macworld (1998-03) (Disk 1).dmg
/
Shareware World
/
Utilities
/
Text Processing
/
Alpha
/
Tcl
/
Modes
/
bibtexMode.tcl
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1997-12-18
|
49.3 KB
|
1,719 lines
|
[
TEXT/ALFA
]
## -*-Tcl-*-
# ###################################################################
# Vince's Additions - an extension package for Alpha
#
# FILE: "bibtexMode.tcl"
# created: 17/8/94 {9:12:06 am}
# last update: 18/12/97 {5:33:11 pm}
# Updated by: Vince Darley
# E-mail: <darley@fas.harvard.edu>
# mail: Division of Engineering and Applied Sciences, Harvard University
# Oxford Street, Cambridge MA 02138, USA
# www: <http://www.fas.harvard.edu/~darley/>
#
# Major rewrite of most of BibTeX mode. Original by Tom Pollard.
# See the end of the BibTeX Help file for a history.
#
# ###################################################################
##
alpha::mode Bib 1.0.1 bibtexMenu {*.bib *.inspec} { texMenu bibtexMenu } {
addMenu bibtexMenu "•282"
} uninstall {this-file} help {file "BibTeX Help"}
# to make sure tex-mode is loaded
texMenu
newPref v bibAutoIndex 1 Bib "" [list "Never make index" \
"Ask user when it is necessary" "Always remake when necessary"] index
newPref v suffixString { \\\\} Bib
newPref v prefixString {% } Bib
newPref v fillColumn {65} Bib
newPref f wordWrap {0} Bib
newPref f autoMark {1} Bib
###########################################################################
# Search patterns for entries and cite-keys
#
# set bibTopPat {^[ ]*@[a-zA-Z]+[\{\(]([-A-Za-z0-9_:/\.]+)}
# match entry type
set bibTopPat {^[ ]*@([a-zA-Z]+)[\{\(]}
# match cite-key
set bibTopPat1 {^[ ]*@[a-zA-Z]+[\{\(][ ]*([^=, ]+)}
# match type and cite-key
set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
# match first field (no cite-key)
set bibTopPat3 {^[ ]*@([a-zA-Z]+)[\{\(]([ ]*[a-zA-Z]+[ ]*=[ ]*)}
newPref v wordBreak {[a-zA-Z0-9]+} Bib
newPref v wordBreakPreface {[^a-zA-Z0-9]} Bib
newPref v funcExpr $bibTopPat Bib
newPref f overwriteBuffer {1} Bib
newPref f fieldBraces {1} Bib
newPref f entryBraces {1} Bib
newPref f segregateStrings {1} Bib
newPref f markStrings {0} Bib
newPref f alignEquals {0} Bib
newPref f zapEmptyFields {0} Bib
newPref f descendingYears {1} Bib
newPref v indentString { } Bib
newPref v stdAbbrevs {jan feb mar apr may jun jul aug sep oct nov dec} Bib
# ◊◊◊◊ Option-click title bar ◊◊◊◊ #
# use TeX routines for Bib mode
proc Bib::OptionTitlebar {} {TeX::OptionTitlebar}
proc Bib::OptionTitlebarSelect {item} {TeX::OptionTitlebarSelect $item}
###########################################################################
# BibTeX Key Bindings.
###########################################################################
# abbreviations: <o> = option, <z> = control, <s> = shift, <c> = command
#
bind 'b' <sz> selectEntry "Bib"
bind 'n' <sz> nextEntry "Bib"
bind 'p' <sz> prevEntry "Bib"
bind 'f' <sz> searchFields "Bib"
bind 'm' <sz> searchEntries "Bib"
bind 'l' <sz> formatEntry "Bib"
###########################################################################
# Data Definitions
###########################################################################
###########################################################################
# Define the data arrays that contain the names of the required,
# optional, and preferred fields for each entry type.
#
# The index names of the rqdFld() array _define_ the valid entry types
# recognized by the program.
#
set rqdFld(article) {author title journal year}
set optFld(article) {volume number pages month note}
set myFld(article) {author title journal volume pages year note}
set rqdFld(book) {author title publisher year}
set optFld(book) {editor volume number series address edition month note}
set rqdFld(booklet) {title}
set optFld(booklet) {author howpublished address month year note}
set rqdFld(conference) {author title booktitle year}
set optFld(conference) {editor volume number series pages organization publisher address month note}
set rqdFld(inBook) {author title chapter publisher year}
set optFld(inBook) {editor pages volume number series address edition month type note}
set rqdFld(inCollection) {author title booktitle publisher year}
set optFld(inCollection) {editor volume number series type chapter pages address edition month note}
set rqdFld(inProceedings) {author title booktitle year}
set optFld(inProceedings) {editor volume number series pages organization publisher address month note}
set rqdFld(manual) {title}
set optFld(manual) {author organization address edition year month note}
set rqdFld(mastersThesis) {author title school year}
set optFld(mastersThesis) {address month note type}
set rqdFld(misc) {}
set optFld(misc) {author title howpublished year month note}
set rqdFld(phdThesis) {author title school year}
set optFld(phdThesis) {address month type note}
set rqdFld(proceedings) {title year}
set optFld(proceedings) {editor volume number series publisher organization address month note}
set rqdFld(techReport) {author title institution year}
set optFld(techReport) {type number address month note}
set rqdFld(unpublished) {author title note}
set optFld(unpublished) {year month}
set entryNames [lsort [array names rqdFld]]
set customEntries [lsort [array names myFld]]
###########################################################################
# Define an array of flags indicating whether the data a given field
# type should be quoted. The actual characters used to quote the field are
# given by $bibOpenQuote and $bibCloseQuote, which are set by the routine
# 'bibFieldDelims' according to the flag $fieldBraces.
#
# Note that the index names of the useBrace() array _define_ the valid
# field types recognized by the program.
#
array set useBrace {
address 1
annote 1
author 1
booktitle 1
chapter 0
crossref 1
edition 1
editor 1
howpublished 1
institution 1
journal 1
key 1
language 1
month 1
note 1
number 0
organization 1
pages 0
publisher 1
school 1
series 1
title 1
type 1
volume 0
year 0
isbn 1
customField 1
city 1
}
set fieldNames [lsort [array names useBrace]]
###########################################################################
# Default values for newly created fields
#
set defFldVal(language) "german"
set fieldDefs [lsort [array names defFldVal]]
###########################################################################
# BibTeX-mode mode definition
###########################################################################
set bibtexKeyWords $fieldNames
regModeKeywords -e {%} -m {@} -c red -k blue Bib $bibtexKeyWords
unset bibtexKeyWords
###########################################################################
# BibTeX Menu Definition.
###########################################################################
proc bibtexMenu {} {}
proc bibtex {} {
global bibtexSig
set name [app::launchAnyOfThese {BIBt Vbib} bibtexSig]
switchTo [file tail $name]
}
menu -n $bibtexMenu {
"bibtex"
"(-)"
{menu -n Entries -p makeEntry {}
}
{menu -n Fields -p makeField {}
}
"(-)"
"selectEntry/B<U<B"
"nextEntry/N<U<B"
"prevEntry/P<U<B"
"formatEntry/L<U<B"
"copyCiteKey/C<U<B"
"(-)"
"searchEntries/M<U<B"
"searchFields/F<U<B"
{menu -n sortBy... -p bibSortProc {
"citeKey"
"firstAuthor,Year"
"lastAuthor,Year"
"year,FirstAuthor"
"year,LastAuthor"
}
}
{menu -n sortMarks... -p markSortProc {
"alphabetically"
"byPosition"
}
}
"(-)"
"countEntries"
"formatAllEntries"
"bibMakeIndex"
"bibMakeDatabase"
}
menu -n Entries -p makeEntry [concat $entryNames {
"(-)"
"customEntry"
} ]
menu -n Fields -p makeField [concat $fieldNames {
"(-)"
"customField"
"multipleFields"
} ]
##
# -------------------------------------------------------------------------
#
# "bib_OpenFile" --
#
# Given a filename, and the directory of the base '.aux' file, try and
# find the file. If we don't succeed, pass the request onto the TeX
# code.
# -------------------------------------------------------------------------
##
proc bib_OpenFile {filename {dir ""}} {
# look where base file was
if {![catch {openFileQuietly "${dir}:${filename}"}]} {
return
}
# look in bibtex inputs folder
global bibtexSig
if {![catch {openFileQuietly "[file dirname [nameFromAppl $bibtexSig]]:BibTeX inputs:${filename}"}]} {
return
}
# look in all usual tex places
openTeXFile "$filename"
return
}
##
# -------------------------------------------------------------------------
#
# "bib_NoEntryExists" --
#
# No entry exists in the known .bib files. Either add an entry, possibly
# in a new bibliography file, or add a .bib file to those currently
# searched.
# -------------------------------------------------------------------------
##
proc bib_NoEntryExists {item {basefile ""}} {
set basefile [bib_getBasefile $basefile]
set choice [prompt \
"No entry '$item' exists. What do you want to do?" \
"New entry" "Choices" \
"New entry" "New entry in new bibliography file" \
"Add .bib file to \\bibliography\{…\}" \
"Change original citation" \
"Search all bibliographies" ]
switch $choice {
"New entry" {
# need to pick a .bib file
set bibfile [bibPickBibliography 1 \
"Select a bibliography file to which to add an entry"]
openTeXFile $bibfile
global entryNames
bibFormatSetup
newEntry [listpick -p "Which type of entry?" $entryNames]
insertText $item
nextTabStop
}
"New entry in new bibliography file" {
set bibfile [putfile "Save new bibliography as…" ".bib"]
if {$bibfile == ""} {
error "No bibliography file selected."
} else {
new -n $bibfile
}
global entryNames
bibFormatSetup
newEntry [listpick -p "Which type of entry?" $entryNames]
insertText $item
nextTabStop
}
"Add .bib file to \\bibliography\{…\}" {
bib_insertNewBibliography $basefile
}
"Search all bibliographies" {
alertnote "Not yet implemented"
}
"Change original citation" {
bib_changeOriginalCitation $item $basefile
}
"Cancel" {
# nothing
}
}
}
proc bib_changeOriginalCitation {citation {basefile ""}} {
if {$basefile == ""} {set basefile [TeX_currentBaseFile]}
# find .aux and open base .tex/.ltx
if {[set proj [isWindowInFileset $basefile "tex"]] != ""} {
set files [texListFilesInFileSet $proj]
} else {
set files $basefile
}
set got "[grep $citation $files]\r"
if {[string first "; Line " $got] == [string last "; Line " $got]} {
# just one match
if ![regexp {∞([^\r\n]*)[\r\n]} $got dmy filename] {
alertnote "I couldn't find the original. You probably have a\
multi-part document which you haven't made into a TeX fileset.\
Unless it's a fileset, I can't find the other files."
return
}
openFileQuietly $filename
eval select [searchInFile $filename $citation 1]
message "This is the original citation. Change it, then re-run LaTeX and BibTeX."
} else {
grepsToWindow "* List of citations *" $got
}
}
proc bib_getBasefile {{basefile ""}} {
if {$basefile == ""} {return [TeX_currentBaseFile]}
# find .aux and open base .tex/.ltx
set base [file root $basefile]
if [file exists ${base}.tex] {
return ${base}.tex
} elseif [file exists ${base}.ltx] {
return ${base}.ltx
} else {
alertnote "Base file with name '${base}.tex/ltx' not found."
error ""
}
}
proc bib_insertNewBibliography {{basefile ""} {bibfile ""}} {
set basefile [bib_getBasefile $basefile]
openFileQuietly ${basefile}
# find bibliography, position cursor and add
pushPosition
endOfBuffer
if [catch {set pos [search -s -f 0 -r 0 -m 0 "\\bibliography\{" [getPos]]}] {
# add the environment
set pos [search -s -f 0 "\\end\{document\}" [getPos]]
goto [lindex $pos 0]
set preinsert "\\bibliography\{"
set postinsert "\}\r\r"
} else {
set preinsert ""
set postinsert ","
goto [lindex $pos 1]
}
if {$bibfile == ""} {
set bibfile [bibPickBibliography 0 \
"Select a bibliography file to add"]
}
insertText "${preinsert}[lindex [split $bibfile "."] 0]${postinsert}"
message "press <Ctrl .> to return to original cursor position"
}
# Used by bibPickBibliography to set a default in the listpick dialog
# It's useful because you will often want to add a bunch of new items
# in a row to the same bibliography.
# NOTE: this is set by my code, not you.
set Bib_defaultBib ""
##
# -------------------------------------------------------------------------
#
# "bibPickBibliography" --
#
# Put up a list-dialog so the user can select a bibliography file for
# some action (taken by the caller). Can also create a new file if
# desired.
# -------------------------------------------------------------------------
##
proc bibPickBibliography {{allowNew 1} {prompt "Pick a bibliography file"}} {
set biblist [bibListAllBibliographies]
if $allowNew {
lappend biblist {New file…}
}
global Bib_defaultBib
set bibfile [listpick -p $prompt -L $Bib_defaultBib $biblist]
if {$bibfile == ""} {
error "No bibliography file selected."
} elseif {$bibfile == "New file…" } {
set bibfile [putfile "Save new bibliography as…" ".bib"]
if {$bibfile == ""} {
error "No bibliography file selected."
} else {
set fout [open $bibfile w]
close $fout
}
}
return [file tail [set Bib_defaultBib $bibfile]]
}
##
# -------------------------------------------------------------------------
#
# "bibListAllBibliographies" --
#
# Return all bibliographies on the search path. Optionally only return
# those which are in a given .aux file.
# -------------------------------------------------------------------------
##
proc bibListAllBibliographies { {auxfile ""} } {
TeXEnsureSearchPathSet
global TeXSearchPath
set biblist {}
if {$auxfile == "" || [catch {set fid [open "$auxfile" r]}]} {
foreach d $TeXSearchPath {
eval lappend biblist [glob -nocomplain ${d}*.bib]
}
} else {
set bibs {}
# get list of bibs from .aux file
set cid [scancontext create]
scanmatch $cid {bibdata\{([^\}]*)\}} {
eval lappend bibs [split $matchInfo(submatch0) ","]
}
scanfile $cid $fid
close $fid
scancontext delete $cid
# find the full paths
foreach b $bibs {
foreach d $TeXSearchPath {
if [file exists ${d}${b}.bib] {
lappend biblist ${d}${b}.bib
break
}
}
}
}
return $biblist
}
##
# -------------------------------------------------------------------------
#
# "bibGotoEntry" --
#
# Look for a bib entry in the given list of files, or if that fails or
# isn't given, look in all available bib files on the search path.
# -------------------------------------------------------------------------
##
proc bibGotoEntry {entry {biblist {}}} {
if ![catch {bib_GotoEntryFromIndex $entry}] {
return
}
if {[llength $biblist] && ![catch {bib_GotoEntry $entry $biblist 0}]} {
return
}
if ![catch {bib_GotoEntry $entry [bibListAllBibliographies]}] {
return
}
beep
error "Can't find entry '$entry' in the .bib file(s)"
}
##
# -------------------------------------------------------------------------
#
# "bib_GotoEntryFromIndex" --
#
# Look in the bibIndex and find an entry very quickly.
# -------------------------------------------------------------------------
##
proc bib_GotoEntryFromIndex {entry} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
global PREFS
# if it fails, but we succeed later, we will have the opportunity
# to rebuild the bibIndex
if [file exists "${PREFS}:bibIndex"] {
source "${PREFS}:bibIndex"
global bibIndex
foreach f [array names bibIndex] {
if [regexp "\[ \r\n\]$entry\[ \r\n\]" "$bibIndex($f)"] {
openFileQuietly $f
set p [search -s -f 1 -r 1 $bibTopPat$entry 0]
eval select $p
refresh
eval select $p
unset bibIndex
return
}
}
unset bibIndex
}
error "Entry '$entry' not found in bibIndex"
}
##
# -------------------------------------------------------------------------
#
# "bib_FindAllEntries" --
#
# Find all entries with a given prefix, optionally attaching the titles
# of the entries (this requires a bibDatabase file to be setup). Used
# by TeX citation completions: \cite{Darley<cmd-Tab>
# -------------------------------------------------------------------------
##
proc bib_FindAllEntries {eprefix {withtitles 1}} {
global PREFS
set matches {}
if $withtitles {
if ![file exists "${PREFS}:bibDatabase"] {
if {[askyesno "No bibDatabase exists, shall I make one?"]=="yes"} {
bibMakeDatabase
} else {
error "No bib database exists"
}
}
set cid [scancontext create]
scanmatch $cid "^${eprefix}" {
lappend matches $matchInfo(line)
}
set fid [open "${PREFS}:bibDatabase" r]
scanfile $cid $fid
close $fid
scancontext delete $cid
} else {
if ![file exists "${PREFS}:bibIndex"] {
if {[askyesno "No bibIndex exists, shall I make one?"]=="yes"} {
bibMakeIndex
} else {
error "No bib index exists"
}
}
global bibIndex
source "${PREFS}:bibIndex"
foreach f [array names bibIndex] {
if { [set matched [univ::modeListCompletions $eprefix "bibIndex(${f})"]] != 0 } {
eval lappend matches $matched
}
}
unset bibIndex
}
return $matches
}
##
# -------------------------------------------------------------------------
#
# "bib_GotoEntry" --
#
# Find a bib entry in one of the given list of files, and signal an
# error if the entry isn't found. I think this is the quickest way.
# -------------------------------------------------------------------------
##
proc bib_GotoEntry {entry biblist {rebuild 1}} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
set cid [scancontext create]
scanmatch $cid $bibTopPat$entry {
set found "$matchInfo(offset)"
}
set found ""
foreach f $biblist {
message "Searching [file tail $f]…"
if {![catch {set fid [open $f]}]} {
scanfile $cid $fid
close $fid
if {$found != ""} {
openFileQuietly $f
goto $found
refresh
select $found [nextLineStart $found]
scancontext delete $cid
global BibmodeVars
# make the index since it was obviously out of date
if {$rebuild == 1 && ($BibmodeVars(bibAutoIndex) == 2 || [askyesno "The bibIndex seems to be out of date. Rebuild?"]=="yes")} {
bibMakeIndex
}
return
}
}
}
scancontext delete $cid
error "Entry '$entry' not found."
}
##
# -------------------------------------------------------------------------
#
# "bibMakeIndex" --
#
# Build the bibIndex file which allows for very fast lookup of bib
# entries.
# -------------------------------------------------------------------------
##
proc bibMakeIndex {} {
global PREFS
set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
set cid [scancontext create]
# this will actually mark strings as well
scanmatch $cid $bibTopPat2 {
if {[string tolower $matchInfo(submatch0)] != "string"} {
lappend found $matchInfo(submatch1)
}
}
set bout [open "${PREFS}:bibIndex" w]
puts $bout "# Bibliography index file for quick reference lookup"
puts $bout "# Created on [mtime [now]]"
foreach f [bibListAllBibliographies] {
set found {}
puts $bout "set \"bibIndex($f)\" \{"
message "Scanning [file tail $f]…"
if {![catch {set fid [open $f]}]} {
scanfile $cid $fid
close $fid
}
# we sort so we can search it efficiently for all entries with
# a given prefix.
puts $bout " [lsort $found] "
puts $bout "\}"
}
close $bout
scancontext delete $cid
message "bibIndex creation complete"
}
##
# -------------------------------------------------------------------------
#
# "bibMakeDatabase" --
#
# Build the bibDatabase which allows speedy completion of citations and
# contains titles, so that you can pick the correct completion easily.
# -------------------------------------------------------------------------
##
proc bibMakeDatabase {} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
global PREFS
set bdatout [open "${PREFS}:bibDatabase" w]
puts $bdatout "# Bibliography database file for quick reference lookup"
puts $bdatout "# Created on [mtime [now]]"
# if it fails, but we succeed later, we will have the opportunity
# to rebuild the bibIndex
foreach f [bibListAllBibliographies] {
message "Scanning ${f}…"
openFileQuietly $f
set p 0
while {![catch {search -s -f 1 -r 1 $bibTopPat $p} epos]} {
set p [lindex $epos 1]
set np [nextLineStart $p]
set entry [string trim [getText $p $np] "\{\( \t\r,"]
if ![catch {search -s -f 1 -r 1 {title[ \t]*=.*,[ \t]*\r} $np} epos] {
set title [eval getText $epos]
regsub -all "\[\r\t\]+" $title { } title
set title [string range $title [string first "=" $title] end]
set title [string trim $title " =\{\}\","]
puts $bdatout "$entry \{$title\}"
set p [lindex $epos 1]
}
}
killWindow
}
close $bdatout
}
###########################################################################
# Menu command procs
###########################################################################
proc makeField {menu item} {
global fieldNames
bibFormatSetup
if {$item == "multipleFields"} {
set flds [listpick -l -L {author year} -p "Pick desired fields:" $fieldNames]
if {[llength flds]} {
set lines {}
foreach fld $flds {
append lines [newField $fld]
}
} else {
return
}
} else {
set lines [newField $item]
}
set pos0 [nextLineStart [getPos]]
goto $pos0
elec::Insertion $lines
}
proc makeEntry {menu item} {
bibFormatSetup
newEntry $item
}
###########################################################################
# Return the bounds of the bibliographic entry surrounding the current
# position.
#
proc getEntry {pos} {
set pos1 [search -f 0 -r 1 -n -s {[ ]*@[a-zA-Z]*[\{\(]} $pos ]
if {$pos1 == ""} {
set begPos [nextLineStart $pos]
set endPos $begPos
} else {
set begPos [lineStart [lindex $pos1 0]]
set pos0 [lindex $pos1 1]
set openBrace [getText [expr $pos0-1] $pos0 ]
if {[catch {matchIt $openBrace $pos0} pos1]} {
alertnote "There seems to be a badly delimited field in here. Are entry and field delimiters set correctly?"
goto $begPos
error "Can't find close brace"
} else {
set endPos [nextLineStart $pos1]
}
}
return [list $begPos $endPos]
}
###########################################################################
# Advance to the next bibliographic entry.
#
proc nextEntry {} {
global bibTopPat bibTopPat1 bibTopPat2
# set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
set pos0 [lindex [getEntry [getPos]] 1]
set nextPos [nextLineStart $pos0]
while {![catch {search -f 1 -r 1 -s $bibTopPat $pos0} pos]} {
regexp $bibTopPat [eval getText $pos] mtch type
if {$type != "string"} {
set nextPos [lindex $pos 0]
break
} else {
set pos0 [nextLineStart [lindex $pos 1]]
}
}
goto $nextPos
}
###########################################################################
# Go back to the previous bibliographic entry.
#
proc prevEntry {} {
global bibTopPat bibTopPat1 bibTopPat2
# set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
set pos0 [lindex [getEntry [getPos]] 0]
if {$pos0 > 0} {
set nextPos $pos0
incr pos0 -1
while {![catch {search -f 0 -r 1 -s $bibTopPat $pos0} pos]} {
regexp $bibTopPat [eval getText $pos] mtch type
if {$type != "string"} {
set nextPos [lindex $pos 0]
break
} else {
set pos0 [lineStart [lindex $pos 0]]
if {$pos0 == 0} {break}
incr pos0 -1
}
}
goto $nextPos
}
}
###########################################################################
# Select (highlight) the current bibliographic entry.
#
proc selectEntry {} {
set pos [getEntry [getPos]]
select [lindex $pos 0] [lindex $pos 1]
}
###########################################################################
# Put the cite-key of the current entry on the clipboard.
#
proc copyCiteKey {} {
global bibTopPat2
set limits [getEntry [getPos]]
set top [lindex $limits 0]
set bottom [lindex $limits 1]
if {[regexp -indices $bibTopPat2 [getText $top $bottom] allofit type citekey]} {
select [expr $top+[lindex $citekey 0]] [expr $top+[lindex $citekey 1]+1]
copy
message "Copied \"[getSelect]\""
}
}
###########################################################################
# Create a new bibliographic entry with its required fields.
#
proc newEntry {entryName} {
global entryNames customEntries fieldNames rqdFld optFld myFld defFldVal
global bibOpenEntry bibCloseEntry BibmodeVars
goto [lindex [getEntry [getPos]] 1]
if {$entryName == "customEntry"} {
set lines "@••$bibOpenEntry••,\r"
set theFields [listpick -l -L {author} -p "Pick desired fields:" $fieldNames]
} else {
set lines "@${entryName}$bibOpenEntry••,\r"
if {[lsearch -exact $customEntries $entryName] >= 0 && [llength $myFld($entryName)]} {
set theFields $myFld($entryName)
} elseif {[lsearch -exact $entryNames $entryName] >= 0} {
set theFields $rqdFld($entryName)
} else {
set theFields {}
}
}
set nmlen 0
foreach field $theFields {
set len [string length $field]
if {$len > $nmlen} {set nmlen $len}
}
set theTop [lineStart [getPos]]
foreach field $theFields {
catch {append lines [newField $field $nmlen]}
}
append lines "$bibCloseEntry\r"
elec::Insertion $lines
}
###########################################################################
# Create a new field within the current bibliographic entry
#
proc newField {fieldName {nmlen 0}} {
global fieldNames useBrace bibOpenQuote bibCloseQuote bibIndent
global fieldDefs defFldVal
set spc " "
if {[lsearch -exact $fieldNames $fieldName] >= 0} {
set needBraces $useBrace($fieldName)
} else {
set needBraces 1
}
if {[lsearch -exact $fieldDefs $fieldName] >= 0} {
set val $defFldVal($fieldName)
} else {
set val "••"
}
if {$nmlen} {
set pad [string range $spc 1 [expr $nmlen - [string length $fieldName]]]
} else {
set pad ""
}
if {$needBraces || $fieldName == "customField"} {
set result "$bibIndent$fieldName =$pad ${bibOpenQuote}${val}${bibCloseQuote},\r"
} else {
set result "$bibIndent$fieldName =$pad $val,\r"
}
return $result
}
proc bibFormatSetup {} {
global bibOpenQuote bibCloseQuote bibIndent BibmodeVars
global bibOpenEntry bibCloseEntry bibAbbrevs
bibFieldDelims
bibEntryDelims
set bibIndent $BibmodeVars(indentString)
regsub {\\t} $bibIndent { } bibIndent
set bibAbbrevs [listStrings]
foreach abbrev $BibmodeVars(stdAbbrevs) {
lappend bibAbbrevs [string tolower $abbrev]
}
}
###########################################################################
# Find all entries that match a given regular expression and copy them to
# a new buffer.
#
proc searchEntries {} {
if [catch {prompt "Regular expression:" ""} reg] return
if {![string length $reg]} return
set reg ^.*$reg.*$
set matches [findEntries $reg]
if {[llength $matches] >0} {
writeEntries $matches 0
} else {
message "No matching entries were found"
}
}
###########################################################################
# Find all entries in which the indicated field matches a given regular
# expression and copy them to a new buffer.
#
proc searchFields {} {
global fieldNames
if {[catch {eval prompt {{Field name:}} "author" {Fields} $fieldNames} fld]} return
if {![string length $fld]} return
if {[catch {prompt "Regular expression:" ""} reg]} return
if {![string length $reg]} return
set matches [findEntries $reg]
if {[llength $matches] == 0} {
return "No matching entries were found"
}
set vals {}
foreach hit $matches {
set pos [lindex $hit 1]
set top [lindex $hit 2]
set bottom [lindex $hit 3]
while {[set failure [expr {[getFldName $pos $top] != $fld}]] &&
![catch {search -f 1 -r 1 -i 1 -m 0 -l $bottom -s -- $reg $pos} mtch]} {
set pos [lindex $mtch 1]
}
if {!$failure} { lappend vals [list $top $bottom] }
}
if {[llength $vals] >0} {
writeEntries $vals 0
} else {
message "No matching entries were found"
}
}
###########################################################################
# Sort all of the entries based on one of various criteria.
#
proc bibSortProc {menu item} {
if {$item == "citeKey"} {
sortByCiteKey
} elseif {$item == "firstAuthor,Year"} {
sortByAuthors 0 0
} elseif {$item == "lastAuthor,Year"} {
sortByAuthors 1 0
} elseif {$item == "year,FirstAuthor"} {
sortByAuthors 0 1
} elseif {$item == "year,LastAuthor"} {
sortByAuthors 1 1
}
}
###########################################################################
# Sort the file marks. (These operations are also available under the
# "Search:NamedMarks" menu)
#
proc markSortProc {menu item} {
if {$item == "alphabetically"} {
sortMarksFile
} elseif {$item == "byPosition"} {
orderMarks
}
}
###########################################################################
# Sort all of the entries in the file alphabetically by author.
#
proc sortByAuthors {{lastAuthorFirst 0} {yearFirst 0}} {
global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
set bibSegStr $BibmodeVars(segregateStrings)
set matches [findEntries $bibTopPat]
set crossrefs [listCrossrefs]
set strings [listStrings]
set vals {}
set others {}
set refs {}
set strs {}
set beg [maxPos]
set end 0
foreach hit $matches {
set pos [lindex $hit 1]
set top [lindex $hit 2]
set bottom [lindex $hit 3]
set entry [getText $top $bottom]
regsub -all "\[\n\r\]+" $entry { } entry
regsub -all "\[ \]\[ \]+" $entry { } entry
regsub {[, ]*[\)\}][ ]*$} $entry { } entry
if {[regexp $bibTopPat1 $entry allofit citeKey]} {
set citeKey [string tolower $citeKey]
set keyExists 1
} else {
set citekey ""
set keyExists 0
}
if {$keyExists && [lsearch -exact $crossrefs $citeKey] >= 0} {
lappend refs [list $pos $top $bottom]
} elseif {$bibSegStr && $keyExists && [lsearch -exact $strings $citeKey] >= 0} {
lappend strs [list $citeKey $top $bottom]
} else {
if {![catch {getFldValue $entry author} fldval]} {
if {[catch {getFldValue $entry year} year]} { set year 9999 }
lappend vals [list [authSortKey $fldval $lastAuthorFirst $year $yearFirst] $top $bottom]
} else {
lappend others [list $pos $top $bottom]
}
}
if {$top < $beg} {set beg $top}
if {$bottom > $end} {set end $bottom}
}
if {$bibSegStr} {
set result [concat $strs $others [lsort $vals] $refs]
} else {
set result [concat $others [lsort $vals] $refs]
}
if {[llength $result] >0} {
writeEntries $result 1 $beg $end
} else {
message "No results of author sort !!??"
}
}
###########################################################################
# Return a list of the cite-keys of all cross-referenced entries.
#
proc listStrings {} {
global bibTopPat bibTopPat1 bibTopPat2
set matches [findEntries {^[ ]*@string *[\{\(]} 0]
message "scanning for @strings…"
foreach hit $matches {
set top [lindex $hit 2]
set bottom [lindex $hit 3]
set entry [getText $top $bottom]
regsub -all "\[\n\r\]+" $entry { } entry
regsub -all "\[ \]\[ \]+" $entry { } entry
regsub {[, ]*[\)\}][ ]*$} $entry { } entry
regexp $bibTopPat1 $entry allofit citekey
set citekey [string tolower $citekey]
if {[catch {incr strings($citekey)} num]} {
set strings($citekey) 1
}
}
if {[catch {lsort [array names strings]} res]} {
set res {}
}
message ""
return $res
}
###########################################################################
# Return a list of the cite-keys of all cross-referenced entries.
#
proc listCrossrefs {} {
set matches [findEntries {crossref}]
catch {unset crossrefs}
message "scanning for crossrefs…"
foreach hit $matches {
set top [lindex $hit 2]
set bottom [lindex $hit 3]
set entry [getText $top $bottom]
regsub -all "\[\n\r\]+" $entry { } entry
regsub -all "\[ \]\[ \]+" $entry { } entry
regsub {[, ]*[\)\}][ ]*$} $entry { } entry
if {![catch {getFldValue $entry crossref} fldval]} {
set fldval [string tolower $fldval]
if {[catch {incr crossref($fldval)} num]} {
set crossrefs($fldval) 1
}
}
}
if {[catch {lsort [array names crossrefs]} res]} {
set res {}
}
message ""
return $res
}
###########################################################################
# Create a sort key from an author list. When sorting entries by author,
# performing the sort using keys should be faster than reparsing the author
# lists for every comparison (the old method :-( ).
#
proc authSortKey {authList lastAuthorFirst {year {}} {yearFirst 0}} {
global BibmodeVars
set pat1 {\\.\{([A-Za-z])\}}
set pat2 {\{([^\{\}]+) ([^\{\}]+)\}}
# Remove enclosing braces, quotes, or whitespace
set auths %[string trim $authList {{}" }]&
# Remove TeX codes for accented characters
regsub -all $pat1 $auths {\1} auths
# Concatenate strings enclosed in braces
while {[regsub -all $pat2 $auths {{\1\2}} auths]} {}
# Remove braces (curly and square)
regsub -all {[][\{\}]} $auths {} auths
# regsub -all {,} $auths { ,} auths
# Replace 'and's with begin-name/end-name delimiters
regsub -all {[ ]and[ ]} $auths { \&% } auths
# Put last name first in name fields without commas
regsub -all {%([^\&,]+) ([^\&, ]+) *\&} $auths {%\2,\1\&} auths
# Remove begin-name delimiters
regsub -all {%} $auths {} auths
# Remove whitespace surrounding name separators
regsub -all {[ ]*\&[ ]*} $auths {\&} auths
# Replace whitespace separating words with shrieks
regsub -all {[ ,]+} $auths {!} auths
# If desired, move last author to head of sort key
if {$lastAuthorFirst} {
regsub {(.*)&([^&]+)&?$} $auths {\2\&\1} auths
}
# If provided, sort by year (descending order) as well
regsub {^[^0-9]*([0-9]*).*$} $year {\1} year
if {$year != {}} {
if {$BibmodeVars(descendingYears)} { catch {set year [expr 9999-$year]} }
if {$yearFirst} {
set auths "$year&$auths"
} else {
regsub {^([^&]+)(&?)} $auths "\\1\\&${year}\\2" auths
}
}
return $auths
}
###########################################################################
# Sort all of the entries in the file alphabetically by their cite-keys.
#
proc sortByCiteKey {} {
global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
set bibSegStr $BibmodeVars(segregateStrings)
set matches [findEntries $bibTopPat]
set crossrefs [listCrossrefs]
set strings [listStrings]
set begEntries [maxPos]
set endEntries 0
set strs {}
set vals {}
set refs {}
foreach hit $matches {
set beg [lindex $hit 0]
set end [lindex $hit 1]
set top [lindex $hit 2]
set bottom [lindex $hit 3]
if {[regexp $bibTopPat1 [getText $top $bottom] allofit citekey]} {
set citekey [string tolower $citekey]
set keyExists 1
} else {
set citekey "000000$beg"
set keyExists 0
}
if {$keyExists && [lsearch -exact $crossrefs $citekey] >= 0} {
lappend refs [list $top $top $bottom]
} elseif {$keyExists && $bibSegStr && [lsearch -exact $strings $citekey] >= 0} {
lappend strs [list $citekey $top $bottom]
} else {
lappend vals [list $citekey $top $bottom]
}
if {$top < $begEntries} {set begEntries $top}
if {$bottom > $endEntries} {set endEntries $bottom}
}
if {$bibSegStr} {
set result [concat $strs [lsort $vals] $refs]
} else {
set result [concat [lsort $vals] $refs]
}
if {[llength $result] >0} {
writeEntries $result 1 $begEntries $endEntries
} else {
message "No results of cite-key sort !!??"
}
}
###########################################################################
# Search for all entries matching a given regular expression. The results
# are returned in a list, each element of which is a list of four integers:
# the beginning and end of the matching entry and the beginning and end of
# the matching string. Adapted from "matchingLines" in "misc.tcl".
#
proc findEntries {reg {casesen 1}} {
if {![string length $reg]} return
set pos 0
set result {}
while {![catch {search -f 1 -r 1 -m 0 -i $casesen -s $reg $pos} mtch]} {
set entry [getEntry [lindex $mtch 0]]
lappend result [concat $mtch $entry]
set pos [lindex $entry 1]
}
return $result
}
###########################################################################
# Return a list containing the data for the current entry, indexed by
# the parameter names, e.g., "author", "year", etc. Index names for the
# entry type and cite-key are "type" and "citekey".
#
proc getFields {pos} {
global bibTopPat bibTopPat1 bibTopPat2 bibTopPat3
set fldPat {[ ]*([a-zA-Z]+)[ ]*=[ ]*}
set limits [getEntry $pos]
set top [lindex $limits 0]
set bottom [lindex $limits 1]
set entry [getText $top $bottom]
regsub -all "\[\n\r\]+" $entry { } entry
regsub -all "\[ \]\[ \]+" $entry { } entry
#
regsub {[, ]*[\)\}][ ]*$} $entry { } entry
if {[regexp -indices $bibTopPat2 $entry mtch theType theKey ]} {
set key [string range $entry [lindex $theKey 0] [lindex $theKey 1]]
set theRest [expr 1 + [lindex $mtch 1]]
} elseif {[regexp -indices $bibTopPat3 $entry mtch theType aField]} {
set key {}
set theRest [lindex $aField 0]
} else {
error "Invalid entry"
}
lappend names type
set type [string tolower [string range $entry [lindex $theType 0] [lindex $theType 1]]]
lappend data [list $type]
lappend names citekey
lappend data $key
set entry ",[string range $entry $theRest end]"
set fldPat {,[ ]*([^ =,]+)[ ]*=[ ]*}
set name {}
while {[regexp -indices $fldPat $entry mtch sub1]} {
set nextName [string range $entry [lindex $sub1 0] [lindex $sub1 1]]
lappend names [string tolower $nextName]
if {$name != ""} {
set prevData [string range $entry 0 [expr [lindex $mtch 0]-1]]
lappend data [breakIntoLines [bibFieldData $prevData]]
}
set name $nextName
set entry [string range $entry [expr [lindex $mtch 1]+1] end]
}
lappend data [breakIntoLines [bibFieldData $entry]]
return [list $names $data]
}
proc bibFieldData {text} {
set text [string trim $text { ,#}]
set text1 [string trim $text {\{\}\" }]
if {[string match {*[\{\}\"]*} $text1]} {
set words [parseWords $text]
if {[llength $words]==1} {
regsub {^[\{\"\']} $text {} text
regsub {[\}\"\']$} $text {} text
}
} else {
set text $text1
}
return $text
}
###########################################################################
# Extract the data from the indicated field of an entry, which is passed
# as a single string. This version tries to be completely general,
# allowing nested braces within data fields and ignoring escaped
# delimiters. (derived from proc getField).
#
proc getFldValue {entry fldname} {
set fldPat "\[ \]*${fldname}\[ \]*=\[ \]*"
set fldPat2 {,[ ]*([^ =,]+)[ ]*=[ ]*}
set slash "\\"
set qslash "\\\\"
set ok [regexp -indices -nocase $fldPat $entry mtch]
if {$ok} {
set pos [expr [lindex $mtch 1] + 1]
set entry [string range $entry $pos end]
if {[regexp -indices $fldPat2 $entry mtch sub1]} {
set entry [string range $entry 0 [expr [lindex $mtch 0]-1]]
}
set fld [bibFieldData $entry]
return $fld
} else {
error "field not found"
}
}
###########################################################################
# Parse the entry around position "pos" and rewrite it to the original
# buffer in a canonical format
#
proc formatEntry {} {
global useBrace bibOpenQuote bibCloseQuote
global bibOpenEntry bibCloseEntry bibIndent
set spc " "
bibFormatSetup
set pos [getPos]
set limits [getEntry $pos]
set top [lindex $limits 0]
set bottom [lindex $limits 1]
if {![catch {bibFormatEntry $pos} result]} {
if {$result != [getText $top $bottom]} {
replaceText $top $bottom $result
}
goto $top
nextEntry
} else {
message "Couldn't format this entry for some reason"
}
}
###########################################################################
# Parse the entry around position "pos" and rewrite it to the original
# buffer in a canonical format
#
proc formatAllEntries {} {
global useBrace bibOpenQuote bibCloseQuote
global bibOpenEntry bibCloseEntry bibIndent
set spc " "
bibFormatSetup
# This little dance handles the case that the first
# entry starts on the first line
#
set hit [getEntry [getPos]]
if {[lindex $hit 0] == [lindex $hit 1]} {
nextEntry
set hit [getEntry [getPos]]
}
while {[getPos] < [lindex $hit 1]} {
set top [lindex $hit 0]
set bottom [lindex $hit 1]
if {![catch {bibFormatEntry $top} result]} {
set oldEntry [getText $top $bottom]
if {$result != $oldEntry} {
deleteText $top $bottom
insertText $result
}
}
goto $top
nextEntry
set hit [getEntry [getPos]]
}
}
###########################################################################
# Parse the entry around position "pos" and rewrite it in a canonical format.
# The formatted entry is returned.
#
proc bibFormatEntry {pos} {
global useBrace bibOpenQuote bibCloseQuote
global bibOpenEntry bibCloseEntry bibIndent
global rqdFld optFld BibmodeVars bibAbbrevs
set spc " "
#
# note: calling proc must call "bibFormatSetup" before calling "bibFormatEntry"
#
set limits [getEntry $pos]
set top [lindex $limits 0]
set bottom [lindex $limits 1]
if {[catch {getFields $pos} flds]} {
error "bibFormatEntry: Getflds couldn't find any"
}
set names [lindex $flds 0]
set vals [lindex $flds 1]
set nfld [llength $names]
set type [string tolower [lindex $vals 0]]
set citekey [lindex $vals 1]
# message "$citekey"
# Don't process @string entries
if {$type == "string"} {
set lines [getText $top $bottom]
return $lines
}
# Find length of longest field name
set nmlen 0
foreach nm $names {
set len [string length $nm]
if {$len > $nmlen} { set nmlen $len }
if {![info exists useBrace($nm)]} { set useBrace($nm) 0 }
}
# Format first line
set lines "@${type}${bibOpenEntry}${citekey},\r"
# Format each field on a separate line
for {set ifld 2} {$ifld < $nfld} {incr ifld} {
set nm [lindex $names $ifld]
set vl [lindex $vals $ifld]
if {$vl != "" || ! $BibmodeVars(zapEmptyFields) ||
[lsearch $rqdFld($type) $nm] >= 0} {
set pad [expr $nmlen - [string length $nm]]
if {$BibmodeVars(alignEquals)} {
set pref "${bibIndent}$nm[string range $spc 1 $pad] ="
} else {
set pref "${bibIndent}$nm =[string range $spc 1 $pad]"
}
set ind [string range $spc 1 [string length $pref]]
# Delimit field, if appropriate
set noBrace [expr ($useBrace($nm) == 0 && [isNum $vl]) || [hasCat $vl]]
if {$noBrace == 0 && [string first " " $vl] < 0} {
set noBrace [expr [lsearch $bibAbbrevs [string tolower $vl]] >= 0]
}
if {$noBrace != 0} {
set vl "$vl,"
} else {
set vl "${bibOpenQuote}${vl}${bibCloseQuote},"
}
set pieces [split $vl "\r"]
append lines "$pref [lindex $pieces 0]\r"
foreach piece [lrange $pieces 1 end] {
append lines "$ind $piece\r"
}
}
}
append lines "$bibCloseEntry\r"
return $lines
}
###########################################################################
# Get the name of the field that starts before the given position,
# $pos. The positions $top and $bottom restrict the range of the
# search for the beginning and end of the field; typically, $top and
# $bottom will be the limits of a given entry.
#
proc getFldName {pos top} {
set fldPat {[, ]+([^ =,\{\}\"\']+)[ ]*=[ ]*}
if {![catch {search -f 0 -r 1 -m 0 -i 1 -s -limit $top "$fldPat" $pos} mtch]} {
set theText [eval getText $mtch]
regexp -nocase $fldPat $theText allofit fldnam
return $fldnam
} else {
return {citekey}
}
}
###########################################################################
# Set the quote characters for quoted fields based on the value of the
# flag $bibUseBrace
#
proc bibFieldDelims {} {
global BibmodeVars bibOpenQuote bibCloseQuote
if {$BibmodeVars(fieldBraces)} {
set bibOpenQuote "{"
set bibCloseQuote "}"
} else {
set bibOpenQuote {"}
set bibCloseQuote {"}
}
}
proc bibEntryDelims {} {
global BibmodeVars bibOpenEntry bibCloseEntry
if {$BibmodeVars(entryBraces)} {
set bibOpenEntry "{"
set bibCloseEntry "}"
} else {
set bibOpenEntry "("
set bibCloseEntry ")"
}
}
proc isBibFile {} {
set fileName [win::Current]
set ext [file extension $fileName]
return [string match ".bib" [string tolower $ext]]
}
proc hasNumVal {str} {
expr ! [catch {expr $str}]
}
proc isNum {str} {
regexp {^[ ]*[0-9]+[ ]*$} $str mtch
}
proc hasCat {str} {
regexp {\#} $str mtch
}
###########################################################################
# Take a list of lists that point to selected entries and copy these into
# a new window. The beginning and ending positions for each entry must
# be the last two items in each sublist. The rest of the sublists are
# ignored. It is assumed that each sublist has the same number of items.
#
proc writeEntries {entryPos nondestructive {beg {0}} {end {-1}}} {
global BibmodeVars
if {$end < 0} {set end [maxPos]}
set llen [expr [llength [lindex $entryPos 0]] - 1]
set llen1 [expr $llen-1]
foreach entry $entryPos {
set limits [lrange $entry $llen1 $llen]
append lines [eval getText $limits]
}
set overwriteOK [expr $nondestructive || ! [isBibFile]]
if {$BibmodeVars(overwriteBuffer) && $overwriteOK} {
deleteText $beg $end
insertText $lines
goto $beg
} else {
set begLines [getText 0 [lineStart $beg]]
set endLines [getText [nextLineStart $end] [maxPos]]
new -n {*BibTeX Sort/Search*} -m Bib
insertText $begLines
insertText $lines
insertText $endLines
goto $beg
setWinInfo dirty 0
catch shrinkWindow
}
}
###########################################################################
# Set a named mark for each entry, using the cite-key name
#
proc Bib::MarkFile {} {
global BibmodeVars
global bibTopPat bibTopPat1 bibTopPat2
set pos 0
while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat1 $pos} res]} {
set start [lindex $res 0]
set end [nextLineStart $start]
set text [getText $start $end]
set lab ""
if {[regexp $bibTopPat2 $text mtch type citekey]} {
if {[string tolower $type] != "string" || $BibmodeVars(markStrings)} {
setNamedMark $citekey [lineStart [expr $start - 1]] $start $start
}
}
set pos $end
}
}
###########################################################################
# Report the number of entries of each type
#
proc countEntries {} {
global entryNames
global bibTopPat bibTopPat1 bibTopPat2
set pos 0
set count 0
catch {unset type}
while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat $pos} res]} {
incr count
set start [lindex $res 0]
set end [nextLineStart $start]
set text [getText $start $end]
set lab ""
if {[regexp $bibTopPat $text mtch entryType]} {
set entryType [string tolower $entryType]
if {[catch {incr type($entryType)} num]} {
set type($entryType) 1
}
}
set pos $end
}
new -n {*BibTeX Statistics*} -m Bib
foreach name [lsort [array names type]] {
if {$type($name) > 0} {
append lines [format "%4.0d %s\n" $type($name) $name]
}
}
append lines "---- -----------------\n"
append lines [format "%4.0d %s\n" $count "Total entries"]
insertText $lines
goto 0
setWinInfo dirty 0
catch {shrinkWindow 1}
}
#--------------------------------------------------------------------------
# command-double-clicking:
#--------------------------------------------------------------------------
###########################################################################
# In Bib mode, Cmd-double-clicks resolve abbrevs and cross-refs
#
proc Bib::DblClick {from to} {
global bibTopPat bibTopPat1 bibTopPat2
set limits [getEntry $from]
set top [lindex $limits 0]
set bottom [lindex $limits 1]
# Extend selection to largest string that could be an entry reference
set text [string trim [eval getText [BibExtendClick $from $to $top $bottom]]]
# Get the citekey of current entry, so we can avoid jumping to it
set citekey {}
regexp $bibTopPat2 [getText $top $bottom] mtch type citekey ]
set fldName [getFldName $from $top]
if {[string length $text] == 0 || $text == $citekey || $fldName == $text ||
($fldName == "citekey" && [string tolower $type] != "string")} {
message "Command-double-click on abbreviations and crossref arguments"
return
}
# Jump to the mark for the specified citation, if a mark exists...
# ...otherwise, do an ordinary search for the cite key
pushPosition
set searchPat "$bibTopPat\[ \]*[quote::Regfind $text]\[ ,\}\)\]"
if {![catch {search -f 1 -r 1 -i 1 -m 0 $searchPat 0} mtch]} {
goto [lindex $mtch 0]
} else {
popPosition
select $from $to
if {$fldName == "crossref"} {
message "Cross-reference \"$text\" not found"
} else {
message "Command-double-click on abbreviations and crossref arguments"
}
return
}
message "Use Ctl-. to return to original position"
return
}
# Extend the selection around the initial selection {$from,$to}
# Extension is restricted to the range {$top,$bottom} (the current entry)
proc BibExtendClick {from to top bottom} {
if {$to == 0} { set to $from }
set result [list $from $to]
if {![catch {search -f 0 -r 1 -s -m 0 -l $top "\[,\{\]\"\'=" $from} mtch0]} {
if {![catch {search -f 1 -r 1 -s -m 0 -l $bottom "\[,\}\]\"\'=" $to} mtch1]} {
set from [lindex $mtch0 1]
set to [lindex $mtch1 0]
# Check for illegal chars embedded in the selection
if {[regexp "\[\{\}\]=" [getText $from $to]] == 0} {
set result [list $from $to]
}
}
}
return $result
}
#===============================================================================
proc pcite {} {
set words [getline "Citation keys" ""]
if {![llength $words]} {error "No keys"}
set pattern {@}
foreach w $words {
append pattern "(\[^@\]+$w)"
}
foreach entry [findEntries $pattern] {
set res [getFields [car $entry]]
set title [lindex [cadr $res] [lsearch [car $res] "title"]]
set citekey [lindex [cadr $res] [lsearch [car $res] "citekey"]]
set matches($title) $citekey
set where($title) [car $entry]
}
if {![info exists matches]} {alertnote "No citations"; return}
set title [listpick -p "Citation?" [lsort [array names matches]]]
putScrap $matches($title)
alertnote $matches($title)
goto $where($title)
}